package com.hksj.ecm.service;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.hksj.cbm.service.DeviceInstanceService;
import com.hksj.cbm.vo.SpaceModelVO;
import com.hksj.ecm.dto.EnergyTrendAnalysisDTO;
import com.hksj.ecm.mapper.EnergyTrendAnalysisMapper;
import com.hksj.ecm.vo.EnergyTrendAnalysisVO;
import com.hksj.ecm.vo.EnergyTrendConsumptionVO;
import com.hksj.ecm.vo.EnergyTrendDeviceVO;
import com.hksj.ecm.vo.YAxisVO;
import com.hksj.tdengine.taos.service.TaosService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zengke
 * @version 1.0
 * @see EnergyTrendAnalysisService
 * @since 2024/8/16 17:36
 */

@Service
public class EnergyTrendAnalysisService {

    @Autowired
    private EnergyTrendAnalysisMapper energyTrendAnalysisMapper;

    @Autowired
    private TaosService taosService;

    @Autowired
    private DeviceInstanceService deviceInstanceService;

    /**
     * 查询能耗趋势分析
     * @param energyTrendAnalysisDTO
     * @return
     */
    public EnergyTrendAnalysisVO queryEnergyTrendAnalysis(EnergyTrendAnalysisDTO energyTrendAnalysisDTO){

        //参数校验
        if (energyTrendAnalysisDTO.getStatisticType() == 1 || energyTrendAnalysisDTO.getStatisticType() == 2){
            //按照楼栋或楼层统计
            Assert.notEmpty(energyTrendAnalysisDTO.getSpaceModelIdList(),"空间模型id集合不为空");
        }
        else if (energyTrendAnalysisDTO.getStatisticType() == 3){
            //按照科室统计
            Assert.notEmpty(energyTrendAnalysisDTO.getDepartmentValueList(),"科室字典值集合不为空");
        } else {
            throw new RuntimeException("请传正确的统计方式");
        }

        //根据前端传值求出x轴的数据
        List<String> timePoints = getTimePoints(energyTrendAnalysisDTO.getStartTime(), energyTrendAnalysisDTO.getEndTime(), energyTrendAnalysisDTO.getTimeType());

        //创建返回的能耗趋势分析结果集
        EnergyTrendAnalysisVO energyTrendAnalysisVO = new EnergyTrendAnalysisVO();

        //设置x轴的数据
        energyTrendAnalysisVO.setXAxis(timePoints);

        //封装y轴的数据
        YAxisVO yAxisVO = new YAxisVO();

        //查询设备列表
        List<EnergyTrendDeviceVO> energyTrendDeviceList = queryDeviceList(energyTrendAnalysisDTO);

        //根据设备列表查询产品
        //将能耗数据设置到y轴
        //获取统计区域列表
        List<String> statisticNameList = energyTrendAnalysisDTO.getStatisticNameList();

        //创建y轴的坐标数据集合 大小跟x轴坐标长度对应
        List<YAxisVO> yAxisVOS = new ArrayList<>(timePoints.size());

        //设备的路径集合
        List<String> pathsList = energyTrendDeviceList.stream().map(EnergyTrendDeviceVO::getSpaceModelPaths).collect(Collectors.toList());

        //设备路径集合不为空,将中文路径设置到设备集合中
        if (!ObjectUtils.isEmpty(pathsList)){
            //查询设备的路径对应的中文map集合
            Map<String, List<SpaceModelVO>> pathsMap = deviceInstanceService.devicePathConvertToUTF8(pathsList);

            //将路径中文设置到每个设备中
            energyTrendDeviceList.forEach(energyTrendDeviceVO -> {

                //设备路径对应的空间模型集合
                List<SpaceModelVO> spaceModelVOList = pathsMap.get(energyTrendDeviceVO.getSpaceModelPaths());
                //使用","拼接空间模型集合名称
                String pathsName = spaceModelVOList.stream().map(SpaceModelVO::getModelName).collect(Collectors.joining(","));

                //将路径中文设置到energyTrendDeviceVO
                energyTrendDeviceVO.setSpaceModelPathsName(pathsName);
            });
        }

        //按照产品id对设备进行分组
        Map<String, List<EnergyTrendDeviceVO>> energyTrendDeviceMap =
                energyTrendDeviceList.stream().collect(Collectors.groupingBy(EnergyTrendDeviceVO::getProductId, Collectors.toList()));

        //由于tdengine无法比较非"yyyy-MM-dd"格式的数据

        String formatPattern;
        switch (energyTrendAnalysisDTO.getTimeType()){
            case 1:
                formatPattern = "yyyy-MM-dd";
                break;
            case 2:
                formatPattern = "yyyy-MM";
                break;
            case 3:
                formatPattern = "yyyy";
                break;
            default:
                formatPattern = "yyyy-MM-dd HH:mm:ss";
                break;
        }

        //计算传入的开始时间的时间范围、结束时间的时间范围
        //若当前时间传入的是 "2024" >>>> 计算时间范围为 "2024-01-01 00:00:00"~"2024-12-31 23:59:59" 依次类推
        Map<String, LocalDate> startTimeDateRange = getDateRange(formatPattern, energyTrendAnalysisDTO.getStartTime());

        Map<String, LocalDate> endTimeDateRange = getDateRange(formatPattern, energyTrendAnalysisDTO.getEndTime());

        //设置开始时间结束时间的范围

        //开始时间为 startTime的范围的最小值
        energyTrendAnalysisDTO.setStartTime(DateUtil.format(DateUtil.date(startTimeDateRange.get("start")),"yyyy-MM-dd 00:00:00"));

        //结束时间为 endTime的范围的最大值
        energyTrendAnalysisDTO.setEndTime(DateUtil.format(DateUtil.date(endTimeDateRange.get("end")),"yyyy-MM-dd 23:59:59"));

        //遍历设备map集合
        energyTrendDeviceMap.forEach((key,value) -> {

            //拼接查询的超级表名称:"properties_" + productId
            String stableName = "properties_".concat(key);

            //检查超级表是否存在
            Boolean existStale = taosService.checkStable("jetlinks_clean_room", stableName);

            if (existStale){
                //存在超级表,查询设备的能耗情况
                List<EnergyTrendConsumptionVO> deviceEnergyList = energyTrendAnalysisMapper.queryDeviceEnergyList(stableName,energyTrendAnalysisDTO);

                //将查询的设备能耗列表设置到设备列表中
                energyTrendDeviceList.forEach(energyTrendDeviceVO -> {
                    deviceEnergyList.forEach(deviceEnergy -> {
                        if (Objects.equals(energyTrendDeviceVO.getDeviceId(),deviceEnergy.getDeviceId())){
                            energyTrendDeviceVO.getEnergyTrendConsumptionList().add(deviceEnergy);
                        }
                    });
                });
            }
        });

        //判断统计方式
        if (energyTrendAnalysisDTO.getStatisticType() == 1 || energyTrendAnalysisDTO.getStatisticType() == 2){
            //按照楼栋、楼层统计
            //遍历统计区域列表
            statisticNameList.forEach(statisticName -> {
                //过滤出在该区域下的设备
                List<EnergyTrendDeviceVO> energyTrendDeviceVOS = energyTrendDeviceList.stream().filter(energyTrendDeviceVO -> energyTrendDeviceVO.getSpaceModelPathsName().contains(statisticName)).collect(Collectors.toList());

                //获取设备能耗列表
                List<EnergyTrendConsumptionVO> energyTrendConsumptionVOS = new ArrayList<>();
                energyTrendDeviceVOS.forEach(energyTrendDeviceVO -> {
                    energyTrendConsumptionVOS.addAll(energyTrendDeviceVO.getEnergyTrendConsumptionList());
                });

                //对设备能耗列表 以时间为单位进行分组,求出能耗的值
                Map<String, Double> energyMap = energyTrendConsumptionVOS.stream().collect(Collectors.groupingBy(EnergyTrendConsumptionVO::getStatisticTime, Collectors.summingDouble(value -> new BigDecimal(value.getConsumptionValue()).doubleValue())));

                YAxisVO yAxis = new YAxisVO();
                yAxis.setStatisticAreaName(statisticName);

                //设置y轴的数据
                timePoints.forEach(timePoint -> {
                    yAxis.getData().add(energyMap.get(timePoint)==null ? "0":String.valueOf(BigDecimal.valueOf(energyMap.get(timePoint)).setScale(2, RoundingMode.HALF_UP)));
                });

                yAxisVOS.add(yAxis);
            });

        }
        else if (energyTrendAnalysisDTO.getStatisticType() == 3){
            //按照科室统计
            //遍历统计区域列表
            statisticNameList.forEach(statisticName -> {
                //过滤出在该区域下的设备
                List<EnergyTrendDeviceVO> energyTrendDeviceVOS = energyTrendDeviceList.stream().filter(energyTrendDeviceVO -> Objects.equals(energyTrendDeviceVO.getDepartmentItemValue(),statisticName)).collect(Collectors.toList());

                //获取设备能耗列表
                List<EnergyTrendConsumptionVO> energyTrendConsumptionVOS = new ArrayList<>();
                energyTrendDeviceVOS.forEach(energyTrendDeviceVO -> {
                    energyTrendConsumptionVOS.addAll(energyTrendDeviceVO.getEnergyTrendConsumptionList());
                });

                //对设备能耗列表 以时间为单位进行分组,求出能耗的值
                Map<String, Double> energyMap = energyTrendConsumptionVOS.stream().collect(Collectors.groupingBy(EnergyTrendConsumptionVO::getStatisticTime, Collectors.summingDouble(value -> new BigDecimal(value.getConsumptionValue()).doubleValue())));

                YAxisVO yAxis = new YAxisVO();
                yAxis.setStatisticAreaName(statisticName);

                timePoints.forEach(timePoint -> {
                    yAxis.getData().add(energyMap.get(timePoint)==null ? "0" : String.valueOf(BigDecimal.valueOf(energyMap.get(timePoint)).setScale(2, RoundingMode.HALF_UP)));
                });

                yAxisVOS.add(yAxis);
            });
        }

        //将y轴数据设置到energyTrendAnalysisVO中
        energyTrendAnalysisVO.setYAxis(yAxisVOS);

        //返回能耗趋势分析结果
        return energyTrendAnalysisVO;
    }

    /**
     * 获取时间段内的时间点
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param timeType 时间类型 1=日 2=月 3=年
     * @return [2022、2023......]
     */
    public List<String> getTimePoints(String startTime,String endTime,Integer timeType){

        Boolean checkResult = checkTimeType(startTime, endTime, timeType);

        if (!checkResult){
            throw new RuntimeException("时间类型与传递的开始结束时间对应不正确!");
        }

        //创建一个时间点集合用于存储所有的时间点
        List<String> timePointList = new ArrayList<>();

        //判断开始时间跟结束时间是否相等 若相等则列表值只有一个
        if (Objects.equals(startTime,endTime)){
            return Collections.singletonList(startTime);
        }

        //不相等
        //将开始时间放入时间点的集合
        timePointList.add(startTime);

        //若时间类型为日 按照日进行取值
        if (timeType == 1){

            //解析开始时间
            DateTime startDateTime = DateUtil.parse(startTime,"yyyy-MM-dd");

            //解析结束时间
            DateTime endDateTime = DateUtil.parse(endTime,"yyyy-MM-dd");

            //判断开始时间是否在结束时间之前
            while (startDateTime.isBefore(endDateTime)){
                //若开始时间在结束时间前 则将当前的开始时间+1
                startDateTime = startDateTime.offset(DateField.DAY_OF_MONTH,1);

                //将当前开始时间按照年月日的格式解析后存入时间点的集合
                timePointList.add(DateUtil.format(startDateTime,"yyyy-MM-dd"));
            }
        }
        //若时间类型为月 按照月进行取值
        else if (timeType == 2){

            //解析开始时间
            DateTime startDateTime = DateUtil.parse(startTime,"yyyy-MM");

            //解析结束时间
            DateTime endDateTime = DateUtil.parse(endTime,"yyyy-MM");

            //判断开始时间是否在结束时间之前
            while (startDateTime.isBefore(endDateTime)){
                //若开始时间在结束时间前 则将当前的开始时间+1
                startDateTime = startDateTime.offset(DateField.MONTH,1);

                //将当前开始时间按照年月日的格式转换后存入时间点的集合
                timePointList.add(DateUtil.format(startDateTime,"yyyy-MM"));
            }
        }
        //若时间类型为年 按照年进行取值
        else if (timeType == 3){

            //解析开始时间
            DateTime startDateTime = DateUtil.parse(startTime,"yyyy");

            //解析结束时间
            DateTime endDateTime = DateUtil.parse(endTime,"yyyy");

            //判断开始时间是否在结束时间之前
            while (startDateTime.isBefore(endDateTime)){
                //若开始时间在结束时间前 则将当前的开始时间+1
                startDateTime = startDateTime.offset(DateField.YEAR,1);

                //将当前开始时间按照年月日的格式转换后存入时间点的集合
                timePointList.add(DateUtil.format(startDateTime, "yyyy"));
            }
        }
        return timePointList;
    }

    /**
     * 校验时间类型是否正确
     * @param startTime
     * @param endTime
     * @param timeType
     * @return
     */
    public Boolean checkTimeType(String startTime,String endTime,Integer timeType){

        try {
            if (timeType == 1){
                //按日
                DateUtil.parseByPatterns(startTime,"yyyy-MM-dd");
                DateUtil.parseByPatterns(endTime,"yyyy-MM-dd");
                return true;
            }
            else if (timeType == 2){
                //按月
                DateUtil.parseByPatterns(startTime,"yyyy-MM");
                DateUtil.parseByPatterns(endTime,"yyyy-MM");
                return true;
            }
            else if (timeType == 3){
                //按年
                DateUtil.parseByPatterns(startTime,"yyyy");
                DateUtil.parseByPatterns(endTime,"yyyy");
                return true;
            }
            else {
                return false;
            }
        }
        catch (Exception e){
            return false;
        }
    }

    /**
     * 查询设备列表
     * @param energyTrendAnalysisDTO
     * @return
     */
    public List<EnergyTrendDeviceVO> queryDeviceList(EnergyTrendAnalysisDTO energyTrendAnalysisDTO){

        List<EnergyTrendDeviceVO> energyTrendDeviceList = energyTrendAnalysisMapper.queryDeviceList(energyTrendAnalysisDTO);

        return energyTrendDeviceList;
    }

    /**
     * 获取时间范围
     * @param dateFormat 时间格式 "yyyy-MM-dd"、"yyyy-MM"、"yyyy"
     * @param input 输入的时间
     * @return 时间范围Map集合
     */
    public static Map<String, LocalDate> getDateRange(String dateFormat, String input) {
        Map<String, LocalDate> range = new HashMap<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);

        try {
            if ("yyyy".equals(dateFormat)) {
                int year = Integer.parseInt(input);
                range.put("start", LocalDate.of(year, 1, 1));
                range.put("end", LocalDate.of(year, 12, 31));
            } else if ("yyyy-MM".equals(dateFormat)) {
                YearMonth yearMonth = YearMonth.parse(input, formatter);
                range.put("start", yearMonth.atDay(1));
                range.put("end", yearMonth.atEndOfMonth());
            } else if ("yyyy-MM-dd".equals(dateFormat)) {
                LocalDate date = LocalDate.parse(input, formatter);
                range.put("start", date);
                range.put("end", date);
            }
        } catch (DateTimeParseException e) {
            System.err.println("Invalid date input: " + e.getMessage());
        }

        return range;
    }


}
